import altdate.TimeSpecification.TimeType;
import altdate.DaySpecification.DayType;

class Test {

	public static function main() {
		var r = new haxe.unit.TestRunner();
		r.add(new TestHaxeDateTools());
		r.add(new TestTools());
		r.add(new TestTimespec());
		r.add(new TestDayspecification());
		r.add(new TestJulian());
		r.add(new TestGregorian());
		r.add(new TestTimeZone());
		r.run();
		//trace(tz.zones.america.Vancouver.changes);
	}

}

class TestHaxeDateTools extends haxe.unit.TestCase {
	function testOne() {
		//trace("===Local zone and Offset which may fail====");
		var d = new Date(2007,07,02,12,12,12);
#if neko
		assertEquals("MDT",DateTools.format(d,"%Z"));
		assertEquals("-0600",DateTools.format(d,"%z"));
#end
		assertEquals("2007",DateTools.format(d,"%Y"));
		//trace("==please report line above==");
	}
	function testDayOfWeek() {
		var d = new Date(2007,07,02,12,12,12);
		assertEquals("4",DateTools.format(d,"%w"));
	}
	function testDayName() {
		var d = new Date(2007,07,02,12,12,12);
#if neko
		assertEquals("Thursday",DateTools.format(d,"%A"));
		assertEquals("Thu",DateTools.format(d,"%a"));
#else
		assertEquals(0,0);
#end
	}
}

class TestTools extends haxe.unit.TestCase {

	function testDayToInt() {
		assertEquals(0,altdate.Tools.dayToInt("Sun"));
		assertEquals(6,altdate.Tools.dayToInt("Sat"));
	}

	function testDayToString() {
		assertEquals("Sun",altdate.Tools.dayToString(0,true));
		assertEquals("Sunday",altdate.Tools.dayToString(0,false));
		assertEquals("Sat",altdate.Tools.dayToString(6,true));
		assertEquals("Saturday",altdate.Tools.dayToString(6,false));
	}


	function testDecimalDayTimeToSeconds() {
		assertEquals(43200,altdate.Tools.decimalDayTimeToSeconds(0.5));
		assertEquals(43200,altdate.Tools.decimalDayTimeToSeconds(1.5));
	}

	function testInt() {
		var a:Int = Math.ceil(-4.55);	// should be -4
		var b:Int = Math.floor(-4.55);  // should be -5
		var c:Int = altdate.Tools.int(-4.55);	// should be -4

		var d:Int = Math.ceil(-4.25);	// should be -4
		var e:Int = Math.floor(-4.25);  // should be -5
		var f:Int = altdate.Tools.int(-4.25);	// should be -4
		assertEquals(-4, a);
		assertEquals(-5, b);
		assertEquals(-4, c);
		assertEquals(-4, d);
		assertEquals(-5, e);
		assertEquals(-4, f);
	}

	function testMonthTo() {
		//neko.Lib.println(here.methodName);
		assertEquals("January", altdate.Tools.monthToString(0));
		assertEquals(0, altdate.Tools.monthToInt("January"));
	}

	function testTimeToSeconds() {
		assertEquals(43200, altdate.Tools.timeToSeconds("12:00:00"));
		// see Tools.hx for explanation
		assertEquals(null, altdate.Tools.timeToSeconds("sandwich"));
	}

	function testSecondsToDecimalDay() {
		assertEquals(0.5, altdate.Tools.secondsToDecimalDay(43200));
	}

	function testSecondsToTime() {
		assertEquals("-00:01:00",altdate.Tools.secondsToTime(-60));
		assertEquals("-00:01:01",altdate.Tools.secondsToTime(-61));
		assertEquals("09:59:59",altdate.Tools.secondsToTime(35999));
		assertEquals("10:00:00",altdate.Tools.secondsToTime(36000));
		assertEquals("24:00:00",altdate.Tools.secondsToTime(86400));
		assertEquals("24:00:01",altdate.Tools.secondsToTime(86401));
		assertEquals("24:01:00",altdate.Tools.secondsToTime(86460));
	}

	function testZeroPadDateNum() {
		assertEquals(" 1", altdate.Tools.zeroPadDateNum(1,true));
		assertEquals("01", altdate.Tools.zeroPadDateNum(1));
		assertEquals("01", altdate.Tools.zeroPadDateNum(1,false));
		assertEquals("11", altdate.Tools.zeroPadDateNum(11,true));
		assertEquals("11", altdate.Tools.zeroPadDateNum(11));
		assertEquals("11", altdate.Tools.zeroPadDateNum(11,false));
	}

	function testNegative() {
		var a:Int = Math.ceil(-4.55);	// should be -4
		var b:Int = Math.floor(-4.55);  // should be -5
		var c:Int = Std.int(-4.55);	// should be -4

		var d:Int = Math.ceil(-4.25);	// should be -4
		var e:Int = Math.floor(-4.25);  // should be -5
		var f:Int = Std.int(-4.25);	// should be -4
		assertEquals(-4, a);
		assertEquals(-5, b);
		assertEquals(-4, d);
		assertEquals(-5, e);
		assertEquals(-4, c);
		assertEquals(-4, f);

	}

	function testPositive() {
		var a:Int = Math.ceil(4.55);	// should be 5
		var b:Int = Math.floor(4.55);  // should be 4
		var c:Int = Std.int(4.55);	// should be 4

		var d:Int = Math.ceil(4.25);	// should be 5
		var e:Int = Math.floor(4.25);  // should be 4
		var f:Int = Std.int(4.25);	// should be 4

		assertEquals(5, a);
		assertEquals(4, b);
		assertEquals(4, c);
		assertEquals(5, d);
		assertEquals(4, e);
		assertEquals(4, f);
	}


	function testOther() {
		var b = Math.floor(4.55);
		var c = Std.int(4.55);
		assertEquals(b,c);
		var a:Float = -4.345;
		assertEquals(-4, altdate.Tools.int(a));
#if (neko || cpp)
		assertEquals("4.345", altdate.Tools.floatToString(4.345));
		assertEquals("-4.345", altdate.Tools.floatToString(-4.345));
#end

	}

}

class TestTimespec extends haxe.unit.TestCase {
	function testTimeSpecParser() {
		var ts = altdate.TimeSpecification.parse("-8:12:28");
		assertEquals(-29548, ts.value);
		assertEquals(WALL, ts.type);
		var ts2 = altdate.TimeSpecification.parse("1:00:00u");
		assertEquals(3600, ts2.value);
		assertEquals(UNIVERSAL, ts2.type);
	}
}

class TestDayspecification extends haxe.unit.TestCase {
	function testParser() {
		var ds1 = altdate.DaySpecification.parse("firstMon");
		var ds2 = altdate.DaySpecification.parse("secondMon");
		var ds3 = altdate.DaySpecification.parse("thirdMon");
		var ds4 = altdate.DaySpecification.parse("fourthMon");
		var ds5 = altdate.DaySpecification.parse("fifthMon");
		var ds6 = altdate.DaySpecification.parse("lastMon");
		var ds7 = altdate.DaySpecification.parse("Thu<=27");
		var ds8 = altdate.DaySpecification.parse("Sun>=2");
		var ds9 = altdate.DaySpecification.parse("Sun>=8");
		assertEquals(2,ds1.getValue(2007,6));
		assertEquals(9,ds2.getValue(2007,6));
		assertEquals(16,ds3.getValue(2007,6));
		assertEquals(23,ds4.getValue(2007,6));
		assertEquals(30,ds5.getValue(2007,6));
		assertEquals(30,ds6.getValue(2007,6));
		assertEquals(26,ds7.getValue(2007,6));
		assertEquals(8,ds8.getValue(2007,6));
		assertEquals(8,ds9.getValue(2007,6));

		assertEquals(7,ds1.getValue(2025,6));
		assertEquals(14,ds2.getValue(2025,6));
		assertEquals(21,ds3.getValue(2025,6));
		assertEquals(28,ds4.getValue(2025,6));
		try{ var tmp = ds5.getValue(2025,6); }
		catch(e:String) {
			assertEquals(
			"No fifth date found for year 2025 month 6",
			e
			);
		}
		assertEquals(28,ds6.getValue(2025,6));
		assertEquals(24,ds7.getValue(2025,6));
		assertEquals(6,ds8.getValue(2025,6));
		assertEquals(13,ds9.getValue(2025,6));
	}
}

class TestJulian extends haxe.unit.TestCase {
	function testCalculate() {
		assertEquals(2400000.0,
			altdate.JulianDay.calculate(1858,10,16,altdate.Tools.timeToSeconds("12:00:00"))
		);
		assertEquals(2500000.0,
			altdate.JulianDay.calculate(2132,7,31,altdate.Tools.timeToSeconds("12:00:00"))
		);
		assertEquals(2454312.0,
			altdate.JulianDay.calculate(
				2007,
				altdate.Tools.monthToInt("July"),
				30,
				altdate.Tools.timeToSeconds("12:00")
			)
		);
		// UNix Epoch
		assertEquals(2440587.5,
			altdate.JulianDay.calculate(
				1970,
				altdate.Tools.monthToInt("January"),
				1,
				altdate.Tools.timeToSeconds("00:00")
			)
		);

	}
	function testConvert() {
		var jd = new altdate.JulianDay(true, 2454312.0);
		assertEquals("2007-07-30 12:00:00",jd.toGregorianDateTime().toString());
		assertEquals("2007-07-30",jd.toGregorianDate().toString());
	}
}

class TestGregorian extends haxe.unit.TestCase {
	function testMath() {
		var gd = new altdate.Gregorian();
		// May 2, 2007
		gd.set(true,null,2007,4,2);
		assertEquals(2454222.5, gd.value);
		gd.value = gd.value + 1;
		assertEquals(2454223.5, gd.value);
		gd.set(true,null,2007,4,2,12);
		assertEquals(2454223.0, gd.value);
		gd.value -= 10.5;
		assertEquals(2454212.5, gd.value);
	}

	function testFromUtcTime() {
		var gd = altdate.Gregorian.fromUtcTime(Date.now().getTime());
		assertEquals("UTC", gd.getTimezone().getAbbrev()[0]);
	}

	function testDateMath() {
		var gd = new altdate.Gregorian();
		gd.set(false,new tz.zones.etc.UTC(),2007,6,31);
		assertEquals("2007-07-31", gd.toString());
		gd.month += 1;
		assertEquals("2007-08-31", gd.toString());
		// here, day 31 of September overflows to Oct 1
		gd.month += 1;
		assertEquals("2007-10-01", gd.toString());

		// adding anything less than a full day will
		// not change a date.
		gd.minutes += 60;
		assertEquals("2007-10-01", gd.toString());
		assertEquals(0, gd.minutes);

		gd.seconds += 86400;
		assertEquals("2007-10-02", gd.toString());
		assertEquals(0, gd.minutes);

		gd.seconds += 86399;
		assertEquals("2007-10-02", gd.toString());
		assertEquals(0, gd.minutes);
	}

	function testString() {
		var gd = new altdate.Gregorian();
		// May 2, 2007
		gd.set(true,null,2007,4,2);
		assertEquals("2007-05-02 00:00:00", gd.toString());
		gd = gd.toDate();
		assertEquals("2007-05-02", gd.toString());
	}

	function testConvert() {
		var gd = new altdate.Gregorian();
		gd.set(true,null,2007,4,2,4,2);
		assertEquals("2007-05-02 04:02:00", gd.toString());
		var jd = gd.toJulianDay();
		var gd2 = jd.toGregorianDateTime();
		assertEquals(gd.toString(), gd2.toString());
	}


	function testHaxeConvert() {
/*
	Since unix timestamps (at least in neko, under linux)
	seem to be adjusted for zone, we can't do this.
		var d = Date.fromString("2007-08-01 12:00:00");
		var gd = altdate.Gregorian.fromTime(d.getTime());
		assertEquals("2007-08-01 12:00:00", gd.toString());
	But we can do this:
*/
		var g = new altdate.Gregorian();
		//g.set(true, new tz.zones.UTC(), 1970,1,1);
		g.set(true, null, 1970,0,1);
		assertEquals(0.0, g.getTime());

		// This here is sheer stupidity, imho. POSIX defines
		// GMT-1 as being the same as UTC+1, basically the inverse.
		// 1 hour has elapsed since the epoch for anyone in UTC-1
		g.set(true, new tz.zones.etc.GMT_plus_1(), 1970,0,1);
		assertEquals(3600.0, g.getTime());
		// 1 hour before the epoch for anyone in UTC+1
		g.set(true, new tz.zones.etc.GMT_minus_1(), 1970,0,1);
		assertEquals(-3600.0, g.getTime());
	}
}

class TestTimeZone extends haxe.unit.TestCase {
	function testOne() {
		var gd = new altdate.Gregorian();
		var zone = new tz.zones.america.Vancouver();
		gd.set(true,zone,2007,4,2,4,2);
		assertEquals(2007, gd.year);
		assertEquals(4, gd.month);
		assertEquals(2, gd.day);
		assertEquals(4, gd.hours);
		assertEquals(2, gd.minutes);
		assertEquals(0, gd.seconds);
	}
	function testYearFormat() {
		var gd = new altdate.Gregorian();
		var zone = new tz.zones.america.Vancouver();
		gd.set(true,zone,2007,4,2,4,2);
		assertEquals("2007", altdate.Tools.format(gd,"%Y"));
	}
	function testDateTimeFormat() {
		var gd = new altdate.Gregorian();
		var zone = new tz.zones.america.Vancouver();
		gd.set(true,zone,2007,4,2,4,2);
		assertEquals("2007-05-02 04:02:00", altdate.Tools.format(gd,"%F %T"));
	}
#if (neko || cpp)
	function testFull() {
		var gd = new altdate.Gregorian();
		var zone = tz.Timezone.get("America/Vancouver");
		gd.set(true,zone,2007,4,2,4,2);
		assertEquals("2007-05-02 04:02:00", altdate.Tools.format(gd,"%F %T"));
	}
#end
}
